﻿using System;
using System.Collections.Generic;
using System.Linq;
using HK.Core.Common.Data;
using HK.Core.Common.Protocol.Data;
using HK.Core.GUIEx.Protocol.Attribute;
using HK.Core.Serializable.Attribute;
using HK.Core.Serializable.Data;
using HK.Core.Serializable.Protocol.Attribute;
using HK.Core.Serializable.Protocol.Data;
using HK.Core.Utils;
using HK.Editor.Settings.Protocol.Data;
using UnityEngine;

namespace HK.Editor.Settings.Data
{

#if PREFAB_SPLIT
    
    /// <summary>
    /// 预制体信息
    /// </summary>
    [Serializable]
    public abstract class PrefabInfo<TInfo> : JsonData<TInfo>, IPrefabInfo, IEquatable<TInfo>
        where TInfo : JsonData, IPrefabInfo
    {
        [SerializeField, PathBrowse("拆分目标", PathBrowseMode.File)]
        private string assetKey = null;
        /// <summary>
        /// 拆分对象
        /// </summary>
        public string AssetKey
        {
            get => assetKey;
            protected set => assetKey = value;
        }
        /// <summary>
        /// 唯一标识Element的Key
        /// </summary>
        public string Key => assetKey;
        
        /// <summary>
        /// 有效标志位
        /// </summary>
        public override bool Valid => !string.IsNullOrEmpty(assetKey);

#region Equal

        /// <summary>
        /// 比较
        /// </summary>
        /// <param name="iOther">Other</param>
        /// <returns>true:相同; false:不同;</returns>
        /// <exception cref="NotImplementedException"></exception>
        public virtual bool Equals(TInfo iOther)
        {
            if (null == iOther || !iOther.Valid) return false;
            return !UtilsString.Diff(assetKey, iOther.AssetKey);
        }

#endregion  
        
        /// <summary>
        /// 应用数据
        /// </summary>
        /// <param name="iData">数据</param>
        /// <param name="iImport">导入标志位(true:从相应的*.json文件导入; false:反之;)</param>
        /// <returns>true:有变更; false:无变更;</returns>
        public override bool ApplyData(IJsonData iData, bool iImport = false)
        {
            if (null == iData || !iData.Valid) return false;

            var dirty = base.ApplyData(iData, iImport);
            if (dirty && !iImport)
            {
                Dirty = true;
            }

            if (iData is IPrefabInfo prefab)
            {
                assetKey = UpdateValue(assetKey, prefab.AssetKey, iImport);
            }

            return Dirty;
        }

        /// <summary>
        /// 清空
        /// </summary>
        public override void Clear()
        {
            base.Clear();
            assetKey = null;
        }
    }

    /// <summary>
    /// 预制体信息
    /// </summary>
    [Serializable]
    public sealed class PrefabInfo : PrefabInfo<PrefabInfo>
    {

#region Create

        /// <summary>
        /// 创建预制体信息
        /// </summary>
        /// <param name="iAssetKey">AssetKey</param>
        /// <returns>预制体信息</returns>
        public static PrefabInfo Create(string iAssetKey)
        {
            return new PrefabInfo { AssetKey = iAssetKey };
        }

#endregion
        
        /// <summary>
        /// 保护构造函数
        /// </summary>
        private PrefabInfo() {}
    }
    
    /// <summary>
    /// 预制体拆分信息
    /// </summary>
    [Serializable]
    public class PrefabSplit : PrefabInfo<PrefabSplit>, IPrefabSplit<PrefabInfo>
    {
        [SerializeField, GenericField("深度", null, "相对根节点的深度。")]
        private int depth = 0;
        /// <summary>
        /// 拆分深度
        /// </summary>
        public int Depth 
        {
            get => depth;
            private set => depth = value;
        }

        [SerializeField, ListView("不拆分节点列表", null, (int)GUIOperateOptions.StrWhiteList, 10)]
        private WhiteList whiteList = HK.Core.Common.Data.WhiteList.Create();
        /// <summary>
        /// 节点白名单
        /// </summary>
        public IWhiteList WhiteList => whiteList;

        /// <summary>
        /// 判断节点名是否包含在节点白名单中
        /// </summary>
        /// <param name="iNodeName">节点名</param>
        /// <returns>true:包含;false:不包含;</returns>
        public bool ContainInWhiteList(string iNodeName)
        {
            if (string.IsNullOrEmpty(iNodeName) || 0 >= whiteList.Count) return false;
            return whiteList.Contains(iNodeName);
        }
        
        [SerializeField, ListView("拆分结果", null, (int)GUIOperateOptions.PrefabsSplitResultPagination, 10)]
        private PrefabsPagination result = PrefabsPagination.Create();
        /// <summary>
        /// 拆分结果列表
        /// </summary>
        public List<string> Result => result.List.Select(iO => iO.AssetKey).ToList();
        
        /// <summary>
        /// 保存拆分结果
        /// </summary>
        /// <param name="iResult">拆分结果列表</param>
        /// <returns>true:成功; false:失败;</returns>
        public bool SaveResult(List<string> iResult)
        {
            if (null == iResult || 0 >= iResult.Count) return false;

            var resultTmp = iResult.Distinct().ToList();
            if (0 >= resultTmp.Count) return false;

            var count = resultTmp.Count;
            for (var idx = 0; idx < count; ++idx)
            {
                var loop = resultTmp[idx];
                if(string.IsNullOrEmpty(loop)) continue;

                var prefab = PrefabInfo.Create(loop);
                if(null == prefab || !prefab.Valid) continue;
                
                result.AddElement(prefab);
            }
            if (0 >= result.Count) return true;
            
            result.Sort(true);
            return true;
        }

#region Equal
        
        /// <summary>
        /// 比较
        /// </summary>
        /// <param name="iOther">Other</param>
        /// <returns>true:相同; false:不同;</returns>
        /// <exception cref="NotImplementedException"></exception>
        public override bool Equals(PrefabSplit iOther)
        {
            if (null == iOther || !iOther.Valid) return false;
            if (!base.Equals(iOther)) return false;
            if (depth != iOther.Depth) return false;
            return UtilsArray.Diff(whiteList.List, iOther.WhiteList.List);
        }

#endregion        

        /// <summary>
        /// 更新值
        /// </summary>
        /// <param name="iCurValue">当前值</param>
        /// <param name="iDstValue">目标值</param>
        /// <param name="iImport">导入标志位</param>
        /// <returns>可以更新的值</returns>
        protected PrefabsPagination UpdateValue(PrefabsPagination iCurValue, List<string> iDstValue, bool iImport)
        {
            var curListTmp = iCurValue.List.Select(iO => iO.AssetKey).ToList();
            var dirty = UtilsArray.Diff(curListTmp, iDstValue);
            if (!dirty) return iCurValue;

            var count = iDstValue.Count;
            var dstPagination = PrefabsPagination.Create();
            for (var idx = 0; idx < count; ++idx)
            {
                var dstValue = iDstValue[idx];
                if(string.IsNullOrEmpty(dstValue)) continue;

                var dstPrefab = PrefabInfo.Create(dstValue);
                if(null == dstPrefab || !dstPrefab.Valid) continue;
                dstPagination.AddElement(dstPrefab);
            }
            
            if (!iImport)
            {
                Dirty = true;
            }

            return dstPagination;
        }     

        /// <summary>
        /// 应用数据
        /// </summary>
        /// <param name="iData">数据</param>
        /// <param name="iImport">导入标志位(true:从相应的*.json文件导入; false:反之;)</param>
        /// <returns>true:有变更; false:无变更;</returns>
        public override bool ApplyData(IJsonData iData, bool iImport = false)
        {
            if (null == iData || !iData.Valid) return false;

            var dirty = base.ApplyData(iData, iImport);
            if (dirty && !iImport)
            {
                Dirty = true;
            }

            if (iData is IPrefabSplit<PrefabInfo> prefab)
            {
                depth = UpdateValue(depth, prefab.Depth, iImport);
                dirty = whiteList.ApplyData(prefab.WhiteList);
                if (dirty && !iImport)
                {
                    Dirty = true;
                }
                result = UpdateValue(result, prefab.Result, iImport);
            }

            return Dirty;
        }

        /// <summary>
        /// 清空
        /// </summary>
        public override void Clear()
        {
            base.Clear();

            depth = 0;
            whiteList.Clear();
            result.Clear();
        }
    }

    /// <summary>
    /// 预制体分页列表
    /// </summary>
    [Serializable]
    public sealed class PrefabsPagination : ListJsonData<PrefabsPagination, PrefabInfo>,
        IPrefabsPagination<PrefabsPagination, PrefabInfo>
    {

#region Create

        /// <summary>
        /// 创建预制体分页列表
        /// </summary>
        /// <returns>预制体分页列表</returns>
        public static PrefabsPagination Create()
        {
            return new PrefabsPagination();
        }

#endregion
        
        /// <summary>
        /// 保护构造函数
        /// </summary>
        private PrefabsPagination() {}
    }

    /// <summary>
    /// 预制体分页列表
    /// </summary>
    [Serializable]
    public sealed class PrefabsSplitPagination : ListJsonData<PrefabsSplitPagination, PrefabSplit>, 
        IPrefabsSplitPagination<PrefabsSplitPagination, PrefabSplit, PrefabInfo>
    {

#region Create

        /// <summary>
        /// 创建预制体拆分列表
        /// </summary>
        /// <returns>预制体拆分列表</returns>
        public static PrefabsSplitPagination Create()
        {
            return new PrefabsSplitPagination();
        }

#endregion
        
        /// <summary>
        /// 保护构造函数
        /// </summary>
        private PrefabsSplitPagination() {}
        
        [SerializeField, GenericField("拆分")]
        private bool split = false;

        /// <summary>
        /// 拆分标志位
        /// </summary>
        public bool Split
        {
            get => split;
            private set => split = value;
        }
        
        /// <summary>
        /// 保存拆分结果
        /// </summary>
        /// <param name="iAssetKey">AssetKey</param>
        /// <param name="iResult">拆分结果列表</param>
        /// <returns>true:成功; false:失败;</returns>
        public bool SaveResult(string iAssetKey, List<string> iResult)
        {
            if (string.IsNullOrEmpty(iAssetKey) || 0 >= iResult.Count) return false;
            if (!Exist(iAssetKey, out var oSplit)) return false;
            if (null == oSplit || !oSplit.Valid) return false;

            return oSplit.SaveResult(iResult);
        }
        
        /// <summary>
        /// 应用数据
        /// </summary>
        /// <param name="iData">数据</param>
        /// <param name="iImport">导入标志位(true:从相应的*.json文件导入; false:反之;)</param>
        /// <returns>true:有变更; false:无变更;</returns>
        public override bool ApplyData(IJsonData iData, bool iImport = false)
        {
            if (null == iData || !iData.Valid) return false;

            var dirty = base.ApplyData(iData, iImport);
            if (dirty && !iImport)
            {
                Dirty = true;
            }

            if (iData is IPrefabsSplitPagination<PrefabsSplitPagination, PrefabSplit, PrefabInfo> prefabs)
            {
                list = UpdateValue(list, prefabs.List, iImport);
            }

            return Dirty;
        }

        /// <summary>
        /// 清空
        /// </summary>
        public override void Clear()
        {
            base.Clear();

            split = false;
            list.Clear();
        }
    }

    /// <summary>
    /// 预制体配置
    /// </summary>
    [Serializable]
    public sealed class PrefabConf : JsonData<PrefabConf>, IPrefabConf
    {

#region Create

        /// <summary>
        /// 创建预制体配置
        /// </summary>
        /// <returns>预制体配置</returns>
        public static PrefabConf Create()
        {
            return new PrefabConf();
        }

#endregion
        
        /// <summary>
        /// 保护构造函数
        /// </summary>
        private PrefabConf() {}
        
#region Split

        [SerializeField, GenericField("拆分", null, "将对结构和层级过于复杂的预制体，按层级深度进行拆分。")]
        private bool split = false;
        /// <summary>
        /// 拆分标志位
        /// </summary>
        public bool Split => split;
        
        [SerializeField, ListView("拆分列表", "assetKey", (int)GUIOperateOptions.PrefabsPagination, 10)]
        private PrefabsSplitPagination splitList = PrefabsSplitPagination.Create();
        /// <summary>
        /// 预制体拆分列表
        /// </summary>
        public IPrefabsSplitPagination<PrefabsSplitPagination, PrefabSplit, PrefabInfo> SplitList => splitList;
        /// <summary>
        /// 拆分标志位
        /// </summary>
        public bool PrefabSplit => splitList.Split;

        /// <summary>
        /// 判断预制体是否为需要拆分的预制体
        /// </summary>
        /// <param name="iAssetKey">AssetKey</param>
        /// <returns>true:是; false:否;</returns>
        public bool IsSplitPrefab(string iAssetKey) => splitList.Exist(iAssetKey, out var oSplit);

        /// <summary>
        /// 取得分割列表
        /// </summary>
        /// <param name="iAssetKey"></param>
        /// <returns></returns>
        public List<string> GetSplitResult(string iAssetKey) =>
            splitList.Exist(iAssetKey, out var oSplit) ? oSplit.Result : null;

        /// <summary>
        /// 保存拆分结果
        /// </summary>
        /// <param name="iAssetKey">AssetKey</param>
        /// <param name="iResult">拆分结果列表</param>
        /// <returns>true:成功; false:失败;</returns>
        public bool SaveSplitResult(string iAssetKey, List<string> iResult) => splitList.SaveResult(iAssetKey, iResult);

#endregion
        
        /// <summary>
        /// 应用数据
        /// </summary>
        /// <param name="iData">数据</param>
        /// <param name="iImport">导入标志位(true:从相应的*.json文件导入; false:反之;)</param>
        /// <returns>true:有变更; false:无变更;</returns>
        public override bool ApplyData(IJsonData iData, bool iImport = false)
        {
            if (null == iData || !iData.Valid) return false;

            var dirty = base.ApplyData(iData, iImport);
            if (dirty && !iImport)
            {
                Dirty = true;
            }

            if (iData is IPrefabConf conf)
            {
                split = UpdateValue(split, conf.Split, iImport);
                dirty = splitList.ApplyData(conf.SplitList);
                if (dirty && !iImport)
                {
                    Dirty = true;
                }
            }

            return Dirty;
        }

        /// <summary>
        /// 清空
        /// </summary>
        public override void Clear()
        {
            base.Clear();

            split = false;
            splitList.Clear();
        }

    }
    
#endif
    
    /// <summary>
    /// 打包配置信息数据
    /// </summary>
    [Serializable]
    public class BuildConfData : SAssetData, IBuildConfData
    {

        [SerializeField, PathBrowse("输出目录", PathBrowseMode.Directory)]
        private string outputDir = null;
        /// <summary>
        /// 本地输出目录
        /// </summary>
        public string OutputDir
        {
            get => outputDir;
            private set => outputDir = value;
        }

#if PREFAB_SPLIT
        
#region Prefab

        [SerializeField, GroupView("预制体设定")]
        private PrefabConf prefabConf = HK.Editor.Settings.Data.PrefabConf.Create();
        /// <summary>
        /// 预制体设定
        /// </summary>
        public IPrefabConf PrefabConf => prefabConf;

        /// <summary>
        /// 拆分标志位
        /// </summary>
        public bool PrefabSplit => prefabConf.PrefabSplit;

        /// <summary>
        /// 判断预制体是否为需要拆分的预制体
        /// </summary>
        /// <param name="iAssetKey">AssetKey</param>
        /// <returns>true:是; false:否;</returns>
        public bool IsSplitPrefab(string iAssetKey) => prefabConf.IsSplitPrefab(iAssetKey);

        /// <summary>
        /// 取得分割列表
        /// </summary>
        /// <param name="iAssetKey"></param>
        /// <returns></returns>
        public List<string> GetSplitResult(string iAssetKey) => prefabConf.GetSplitResult(iAssetKey);

        /// <summary>
        /// 保存拆分结果
        /// </summary>
        /// <param name="iAssetKey">AssetKey</param>
        /// <param name="iResult">拆分结果列表</param>
        /// <returns>true:成功; false:失败;</returns>
        public bool SaveSplitResult(string iAssetKey, List<string> iResult) => prefabConf.SaveSplitResult(iAssetKey, iResult);

#endregion
        
#endif
        
#region App

        [SerializeField, GroupView("App设定")]
        private BuildAppConf appConf = BuildAppConf.Create();
        /// <summary>
        /// App打包配置
        /// </summary>
        public IBuildAppConf AppConf => appConf;

        /// <summary>
        /// App版本
        /// </summary>
        public ISAssetVersion AppVersion => appConf?.Version;

        /// <summary>
        /// 重置App版本
        /// </summary>
        /// <param name="iVersion">App版本</param>
        /// <returns>true:有变化; false:无变化;</returns>
        public bool ResetAppVersion(string iVersion) => appConf?.ResetVersion(iVersion) ?? false;

#endregion

#region Version

        [SerializeField, GroupView("资源设定")]
        private BuildResourceConf resourceConf = BuildResourceConf.Create();
        /// <summary>
        /// 打包版本配置信息
        /// </summary>
        public IBuildResourceConf ResourceConf => resourceConf;

        /// <summary>
        /// 母包版本
        /// </summary>
        public ISAssetVersion OriginVersion => resourceConf?.OriginVersion;

        /// <summary>
        /// 重置母包版本
        /// </summary>
        /// <param name="iVersion">母包版本</param>
        /// <returns>true:有变化; false:无变化;</returns>
        public bool ResetOriginVersion(string iVersion) => resourceConf?.ResetOriginVersion(iVersion) ?? false;
        
        /// <summary>
        /// 上一母包版本
        /// </summary>
        public ISAssetVersion LastOriginVersion => resourceConf?.LastOriginVersion;
        
        /// <summary>
        /// 重置上一次母包版本
        /// </summary>
        /// <param name="iVersion">母包版本</param>
        /// <returns>true:有变化; false:无变化;</returns>
        public bool ResetLastOriginVersion(string iVersion) => resourceConf?.ResetLastOriginVersion(iVersion) ?? false;
        
        /// <summary>
        /// 补丁版本
        /// </summary>
        public ISAssetVersion PatchVersion => resourceConf?.PatchVersion;
        
        /// <summary>
        /// 重置补丁版本
        /// </summary>
        /// <param name="iVersion">母包版本</param>
        /// <returns>true:有变化; false:无变化;</returns>
        public bool ResetPatchVersion(string iVersion) => resourceConf?.ResetPatchVersion(iVersion) ?? false;
        
        /// <summary>
        /// 上一补丁版本
        /// </summary>
        public ISAssetVersion LastPatchVersion => resourceConf?.LastPatchVersion;
        
        /// <summary>
        /// 重置上一次母包版本
        /// </summary>
        /// <param name="iVersion">母包版本</param>
        /// <returns>true:有变化; false:无变化;</returns>
        public bool ResetLastPatchVersion(string iVersion) => resourceConf?.ResetLastPatchVersion(iVersion) ?? false;
        
#endregion

        /// <summary>
        /// 应用数据
        /// </summary>
        /// <param name="iData">数据</param>
        /// <param name="iImport">导入标志位(true:从相应的*.json文件导入; false:反之;)</param>
        /// <returns>true:有变更; false:无变更;</returns>
        public override bool ApplyData(IJsonData iData, bool iImport = false)
        {
            if (null == iData || !iData.Valid) return false;

            var dirty = base.ApplyData(iData, iImport);
            if (dirty && !iImport)
            {
                Dirty = true;
            }

            if (iData is IBuildConfData conf)
            {
                outputDir = UpdateValue(outputDir, conf.OutputDir, iImport);
                
                dirty = appConf.ApplyData(conf.AppConf);
                if (dirty && !iImport)
                {
                    Dirty = true;
                }
                
                dirty = resourceConf.ApplyData(conf.ResourceConf);
                if (dirty && !iImport)
                {
                    Dirty = true;
                }
            }
            
            return Dirty;
        }
    
        /// <summary>
        /// 清空
        /// </summary>
        public override void Clear()
        {
            base.Clear();
            
            outputDir = null;
            appConf.Clear();
            resourceConf.Clear();
        }
    }
}
